/*
 * Created by SharpDevelop.
 * User: David
 * Date: 2006-11-26
 * Time: 23:21
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections.Generic;
using System.Drawing;

using Labb4.Media.Graphics;
using Labb4.Pooman;

namespace Labb4.Entity
{
	/// <summary>
	/// A graphical entity represented by a sprite.
	/// </summary>
	public class SpriteEntity : BasicEntity
	{
		#region members
		
		/// <summary>
		/// Reference to the renderer
		/// </summary>
		private RenderManager						m_RenderManager = RenderManager.Instance;
		
		/// <summary>
		/// A dictionary storing the available animations and their respective names
		/// </summary>
		protected Dictionary<string, Animation>		m_Animations;
		/// <summary>
		/// A dictionary storing the avilable animation names in the order they are added
		/// </summary>
		private Dictionary<int, string>				m_AnimationNumbers;
		/// <summary>
		/// A queue of upcoming animations
		/// </summary>
		private List<Animation>						m_AnimationQueue;
		
		/// <summary>
		/// The picture with the sprite map for this sprite entity
		/// </summary>
		//private Picture								m_PictureData;
		
		/// <summary>
		/// Number of frames fitting in on one line
		/// </summary>
		private readonly int						m_FramesPerLine;
		/// <summary>
		/// Number of lines of frames
		/// </summary>
		private readonly int						m_LinesOfFrames;
		/// <summary>
		/// The number of the current frame, where 0 is the first frame
		/// </summary>
		private int									m_CurrentFrame;
		
		#endregion
		
		#region constructors
		
		/// <summary>
		/// Constructor, calls base, initializes members
		/// </summary>
		/// <param name="picture">The picture storing the sprite map for this sprite entity</param>
		/// <param name="framesPerLine">Number of frames fitting in on one line</param>
		/// <param name="linesOfFrames">Number of lines of frames in this entity's sprite map</param>
		/// <param name="x">The x coordinate for this entity</param>
		/// <param name="y">The y coordinate for this entity</param>
		/// <param name="width">This entity's width</param>
		/// <param name="height">This entity's height</param>
		/// <param name="velocity">The starting velocity of this entity</param>
		/// <param name="direction">The initial direction of this entity</param>
		public SpriteEntity(Picture picture, int framesPerLine, int linesOfFrames, float x, float y, int width, int height, float velocity, float direction) :
			base(x, y, width, height, velocity, direction, picture)
		{
			m_Animations = new Dictionary<string, Animation>();
			m_AnimationNumbers = new Dictionary<int, string>();
			m_AnimationQueue = new List<Animation>();
			
			//m_PictureData = picture;
			m_FramesPerLine = framesPerLine;
			m_LinesOfFrames = linesOfFrames;
			
			m_CurrentFrame = 0;
		}
		
		#endregion
		
		#region overrides
		
		/// <summary>
		/// Adds an animation to the collection of existing animations
		/// </summary>
		/// <param name="name">The name for this animation</param>
		/// <param name="animation">The animation</param>
		public override void AddAnimation(string name, Animation animation)
		{
			m_Animations.Add(name, animation);
			m_AnimationNumbers.Add(m_AnimationNumbers.Count, name);
		}
		
		/// <summary>
		/// Selects an animation to be played. This animation is pushed on to the queue of
		/// upcoming animations
		/// </summary>
		/// <param name="name">The name of the animation to select</param>
		public override void SelectAnimation(string name)
		{
			if(m_Animations.ContainsKey(name))
			{
				if(m_AnimationQueue.Count == 0)
				{
					m_AnimationQueue.Add(m_Animations[name]);
				}
				else
				{
					if(m_AnimationQueue[0] != m_Animations[name])
					{
						m_AnimationQueue.Add(m_Animations[name]);
						
						if(!m_AnimationQueue[0].MustFinish)
						{
							if(m_AnimationQueue.Count > 1)
							{
								m_AnimationQueue.RemoveAt(0);
								m_CurrentFrame = 0;
							}
						}
					}
				}
			}
		}
		
		/// <summary>
		/// Selects an animation to be played. This animation is pushed on to the queue of
		/// upcoming animations
		/// </summary>
		/// <param name="i">The number of the animation to choose, numbers are ordered by
		/// the order they're input </param>
		public override void SelectAnimation(int i)
		{
			if(m_AnimationNumbers.ContainsKey(i))
			{
				SelectAnimation(m_AnimationNumbers[i]);
			}
		}
		
		/// <summary>
		/// Kills the sprite entity by displaying a death animation. When the death animation has
		/// finished, the entity is removed.
		/// </summary>
		/// <param name="deathAnimation">The name of the animation to display as the entity's final animation</param>
		public override void Die(string deathAnimation)
		{
			SelectAnimation(deathAnimation);
			TimeToDie = EntityManager.Instance.CurrentTime + (int)((float)m_Animations[deathAnimation].Length * ((float)m_Animations[deathAnimation].Count - 0.5f));
		}
		
		/// <summary>
		/// <see cref="Entity.Update">Updates the entity's position</see>
		/// Also updates the animation, if there's an animation waiting in line. If so, the current
		/// animation is stopped, and the new animation starts, UNLESS the current animation has the
		/// flag MustFinish set.
		/// </summary>
		/// <param name="deltaTime"></param>
		public override void Update(ref int deltaTime)
		{
			base.Update(ref deltaTime);
			
			if(m_Active)
			{
				if(m_AnimationQueue.Count > 0)
				{
					if(m_Counter > m_AnimationQueue[0].Length)
					{
						m_CurrentFrame++;
						m_Counter -= m_AnimationQueue[0].Length;
						
						if(m_CurrentFrame == m_AnimationQueue[0].Count)
						{
							if(m_AnimationQueue.Count > 1)
							{
								m_AnimationQueue.RemoveAt(0);
							}
							
							m_CurrentFrame = 0;
						}
					}
				}
			}
		}
		
		/// <summary>
		/// Checks if the currently displayed frame has color in the asked point.
		/// <see cref="PictureEntity.PointIsColor">See PictureEntity</see> or <see cref="Picture">Picture</see>
		/// </summary>
		/// <param name="x">The screen relative x coordinate to check</param>
		/// <param name="y">The screen relative y coordinate to check</param>
		/// <returns></returns>
		public override bool PointIsColor(ref int x, ref int y)
		{
			Rectangle rect = GetCurrentFrame();
			
			int relX = rect.X + (x - (int)m_Xpos);
			int relY = rect.Y + (y - (int)m_Ypos);
			
			return m_PictureData.PointIsColor(ref relX, ref relY);
			//return m_PictureData.PointIsColor(rect.X + (x - (int)m_Xpos), rect.Y + (y - (int)m_Ypos));
		}
		
		/// <summary>
		/// Draws the current frame of the sprite entity to the screen
		/// </summary>
		public override void Draw()
		{
			if(m_Active)
			{
				if(m_AnimationQueue.Count > 0)
				{
					Rectangle source = GetCurrentFrame();
					Rectangle destination = new Rectangle();
					
					destination.X = (int)m_Xpos;
					destination.Y = (int)m_Ypos;
					destination.Width = m_Width;
					destination.Height = m_Height;
					
					m_RenderManager.DrawPicture(m_PictureData, destination, source);
				}
			}
		}
		
		#endregion
		
		#region private methods
		
		/// <summary>
		/// Determines the coordinates of the current frame on the sprite map, relative to the sprite map
		/// itself. The first frame (frame 0) should thus have x and y coordinates (0, 0)
		/// </summary>
		/// <returns>Rectangle pointing to the current frame</returns>
		private Rectangle GetCurrentFrame()
		{
			int line = m_AnimationQueue[0][m_CurrentFrame] / m_FramesPerLine;
			int frameOnLine = m_AnimationQueue[0][m_CurrentFrame] - (line * m_FramesPerLine);
			
			Rectangle rect = new Rectangle();
			
			rect.X = frameOnLine * m_Width;
			rect.Y = line * m_Height;
			rect.Width = m_Width;
			rect.Height = m_Height;
			
			return rect;
		}
		
		#endregion
	}
}
